﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.CodeDom;
using System.Reflection;
using System.Text.RegularExpressions;
using devtm.Editor.TypeSelector;
using System.Collections.ObjectModel;
using devtm.AutoMapper.CustomCode;
using CodeDomHelper.Types;
using CodeDomHelper.CodeDom;
using System.ComponentModel;
using System.Diagnostics;
using System.Collections;
using System.ComponentModel.Composition;
using CustomTypes.Mono.Serializers;



namespace devtm.AutoMapper
{

    [CLSCompliant(false)]
    public class AutoMapperCodeDomGenerator
    {

        public string[] keyWords = new string[] { "abstract", "event", "new", "struct", "as", "explicit", "null", "switch", "base", "extern", "object", "this", "bool", "false", "operator", "throw", "break", "finally", "out", "true", "byte", "fixed", "override", "try", "case", "float", "params", "typeof", "catch", "for", "private", "uint", "char", "foreach", "protected", "ulong", "checked", "goto", "public", "unchecked", "class", "if", "readonly", "unsafe", "const", "implicit", "ref", "ushort", "continue", "in", "return", "using", "decimal", "int", "sbyte", "virtual", "default", "interface", "sealed", "volatile", "delegate", "internal", "short", "void", "do", "is", "sizeof", "while", "double", "lock", "stackalloc", "else", "long", "static", "enum", "namespace", "string" };

        private string AutoMapperName = string.Empty;
        This MyThis = new This(new TypeExp(typeof(object)));

        VariableExp VariableAutomapper;

        private Dictionary<string, IndexBag> IndexexDic = new Dictionary<string, IndexBag>();
        private Dictionary<string, VariableExp> _externalMappers = new Dictionary<string, VariableExp>();

        PropertyExp PerCounterClass;
        PropertyExp PerCounterProperties;

        MethodInvoke PerCounterIncrementClass;
        MethodInvoke PerCounterDecrementClass;

        MethodExp PerCounterIncrementProperties;
        PropertyExp PerCounterDecrementProperties;

        public AutoMapperCodeDomGenerator(string name)
        {

            AutoMapperName = name;

            VariableAutomapper = new VariableExp(AutoMapperName, TypeExp.Object);

            PerCounterClass = VariableAutomapper.Property("counterClass", TypeExp.Object);

            PerCounterIncrementClass = PerCounterClass.Method("Increment").Call();
            PerCounterDecrementClass = PerCounterClass.Method("Decrement").Call();

            PerCounterProperties = VariableAutomapper.Property("counterProperties", TypeExp.Object);
            PerCounterIncrementProperties = PerCounterProperties.Method("IncrementBy");

            PerCounterDecrementProperties = PerCounterProperties.Property("RawValue", Helper.Long);

        }

        private int _cnt = 0;
        internal CodeCompileUnit Generate(CodeCompileUnit code, MappingModel model)
        {

            this.Model = model;
            integratedMapper = this.Model.Scenarii.Split('|').Where(c => !string.IsNullOrEmpty(c)).ToList();

            _cnt = 0;
            using (new RegionExp(code, "Mappers"))
            {

                string n = model.Namespace;
                NamespaceExp elementNamespace = new NamespaceExp(n);
                code.Namespaces.Add(elementNamespace);

                TypeMember Class = Helper.CreateClass(AutoMapperName, true, false);
                Class.IsPartial = true;
                elementNamespace.Types.Add(Class);
                MethodMember MyCleanUp = null;

                if (Model.GenerateClear) 
                    MyCleanUp = Class.Members.CreateMethod("Clear", MemberVisibility.Public, false);
                

                #region // ------------ counter perf ------------
                if (model.Instrumentation)
                    CreatePerfCounters(elementNamespace, Class);
                #endregion// ------------ counter perf ------------


                #region// --------------- Indexes --------------
                List<String> ls = GetIndexes(model).Distinct().ToList();
                if (ls.Count > 0)
                {
    
                    System.Text.RegularExpressions.Regex regEx = new Regex(@"[^\w]");

                    foreach (string item in ls)
                    {

                        string[] tb = item.Split('§');

                        TypeExpression tname1 = (TypeExpression)tb[0];
                        TypeExpression tname2 = (TypeExpression)tb[1];

                        Hashtable l = new Hashtable();

                        TypeExp t = new TypeExp(typeof(Hashtable));
                        string name = regEx.Replace(tname1.FullName + "_" + tname2.FullName, "");
                        var v = new VariableExp(name + "Dic" + Guid.NewGuid().ToString().Replace("-", "").Substring(0, 5), t);
                        IndexBag b = new IndexBag() { Variable = v };
                        IndexexDic.Add(item, b);

                        var myMember = Class.Members.CreateFieldAutoInit(v.VariableName, MemberVisibility.Public, false, t);                        

                        VariableExp v1 = new VariableExp(myMember.Name, t);

                        if (MyCleanUp != null)
                            MyCleanUp.Statements.Add(v1.Method("Clear").Call());

                    }
                }
                #endregion// --------------- Indexes --------------


                #region// -------------- Mappeurs --------------

                HashSet<Mapper> mappers = GetGenerateMappers();

                foreach (Mapper mapper in mappers)
                {

                    if (integratedMapper.Count == 0 || integratedMapper.Contains(mapper.Name))
                        if (mapper.MappingSource != null && mapper.MappingDestination != null)
                            if (mapper.Generate)
                            {
                                devtm.AutoMapper.Helpers._output.WriteLine(string.Format("Generate mapper {0}.{1} to {2}.{3}", mapper.MappingSource.TypeElement.Namespace, mapper.MappingSource.TypeElement.Name, mapper.MappingDestination.TypeElement.Namespace, mapper.MappingDestination.TypeElement.Name));
                                try
                                {
                                    GenerateMapper(elementNamespace, mapper);
                                }
                                catch (Exception e)
                                {
                                    devtm.AutoMapper.Helpers._output.WriteLine(string.Format("Fail on generate mapper {0}.{1} to {2}.{3}", mapper.MappingSource.TypeElement.Namespace, mapper.MappingSource.TypeElement.Name, mapper.MappingDestination.TypeElement.Namespace, mapper.MappingDestination.TypeElement.Name));
                                    StackTrace trace = new StackTrace(e, true);
                                    throw;
                                }
                            }
                }

                #endregion// -------------- Mappeurs --------------



                foreach (var item in _externalMappers)
                {
                    Class.Members.CreateFieldAutoInit(item.Value.VariableName, MemberVisibility.Public, false, item.Value.Type);
                    
                    if (MyCleanUp != null)
                        MyCleanUp.Statements.Add(item.Value.Method("Clear").Call());

                }   

                // insere les namespaces
                var p = elementNamespace.GetNamespaces(3, n);
                foreach (CodeNamespaceImport item in p)
                    elementNamespace.Imports.Add(item);

            }

            
            return code;
        }

        private HashSet<Mapper> GetGenerateMappers()
        {
            HashSet<Mapper> mappers = new HashSet<Mapper>();
            foreach (Mapper mapper in Model.Mappers)
                if (integratedMapper.Count == 0 || integratedMapper.Contains(mapper.Name))
                    if (mapper.MappingSource != null && mapper.MappingDestination != null)
                        if (mapper.Generate)
                            if (!mappers.Contains(mapper))
                            {
                                mappers.Add(mapper);
                                GetMappersInRecursifProperties(mapper, new Stack<Mapper>(), mappers);
                            }
            
            return mappers;
        }

        private void GetMappersInRecursifProperties(Mapper m, Stack<Mapper> stack, HashSet<Mapper> list)
        {

            stack.Push(m);

            foreach (ItemMap item in m.ItemMaps)
            {

                if (!(item.SourceIsClass || item.SourceIsInterface) || !item.IsRecursif)
                    continue;

                #region GetMapper
                
                string typeSource = string.Empty;
                if (item.SourceIsEnumerable)
                {
                    TypeExpression t = BuildTypeExpression.Analyse(item.SourceType);
                    typeSource = t.IsGeneric
                    ? t.SubType.AssemblyQualifiedName.Replace("[]", "")
                    : t.AssemblyQualifiedName.Replace("[]", "");
                }
                else
                    typeSource = item.SourceType;


                string typeTarget = string.Empty;
                if (item.TargetIsEnumerable)
                {
                    TypeExpression t = BuildTypeExpression.Analyse(item.TargetType);
                    typeTarget = t.IsGeneric
                    ? t.SubType.AssemblyQualifiedName.Replace("[]", "")
                    : t.AssemblyQualifiedName.Replace("[]", "");
                }
                else
                    typeTarget = item.TargetType;

                Mapper m2 = Model.FindMapperFor(typeSource, typeTarget);

                #endregion

                if (m2 != null)
                    if (!stack.Contains(m2))
                        if (!list.Contains(m2))
                        {
                            list.Add(m2);
                            GetMappersInRecursifProperties(m2, stack, list);
                        }
            }

            stack.Pop();

        }



        private IEnumerable<string> GetIndexes(MappingModel model)
        {

            foreach (var m in model.Mappers)
            {

                if (m.ReferenceMultiple)
                    yield return m.MappingSource.TypeElement.FullName + "§" + m.MappingDestination.TypeElement.FullName;

                foreach (var p in m.ItemMaps)
                    if (p.IsRecursif)
                    {

                        var ss1 = BuildTypeExpression.Analyse(p.SourceType);
                        var tt1 = BuildTypeExpression.Analyse(p.TargetType);

                        yield return BuildIndexKey(ss1, tt1);

                    }

            }

        }

        private static string BuildIndexKey(TypeExpression ss1, TypeExpression tt1)
        {

            string sss1 = ss1.IsGeneric
                ? ss1.SubType.FullName.Replace("[]", "")
                : ss1.FullName.Replace("[]", "");

            string ttt1 = tt1.IsGeneric
                ? tt1.SubType.FullName.Replace("[]", "")
                : tt1.FullName.Replace("[]", "");

            return sss1 + "§" + ttt1;

        }

        private IEnumerable<ItemMap> GetItemMap(MappingModel model, string FullName)
        {

            foreach (var m in model.Mappers)
                foreach (var p in m.ItemMaps)
                    if (p.IsRecursif)
                        yield return p;

        }



        #region Perf counters

        private void CreatePerfCounters(NamespaceExp elementNamespace, TypeMember Class)
        {

            var categ = Helper.Primitive(Model.Category);
            
            var t = new TypeExp(typeof(PerformanceCounter));


            var counterClass = Class.Members.CreateFielWithInit
                (
                    "counterClass",
                    MemberVisibility.InternalProtected,
                    false,
                    t,
                    VariableAutomapper.Method("InitializeClass", t).Call()
                ).Static();

            var counterProperties = Class.Members.CreateFielWithInit
                (
                    "counterProperties",
                    MemberVisibility.InternalProtected,
                    false,
                    t,
                    VariableAutomapper.Method("InitializeProperties", t).Call()
                ).Static();


            VariableExp performanceCounterCategory = new VariableExp("PerformanceCounterCategory", TypeExp.Object);



            var methodProperties = Class.Members.CreateMethod("InitializeClass", MemberVisibility.InternalProtected, false).Static();
            methodProperties.ReturnType = t;
            var p1 = Helper.Primitive("Created class");
            var a1 = performanceCounterCategory.Method("Exists", Helper.Bool).Call(categ);
            var b1 = performanceCounterCategory.Method("CounterExists", Helper.Bool).Call(p1, categ);
            var Try1 = methodProperties.Statements.Try();
            Try1.CatchClauses.Catch("e", new TypeExp(typeof(Exception)));
            var If1 = Try1.TryStatements.If(a1);
            var If12 = If1.TrueStatements.If(b1);            
            If12.TrueStatements.Return(counterProperties.New(categ, p1, Helper.False));
            methodProperties.Statements.Return(Helper.Null);


            var methodClass = Class.Members.CreateMethod("InitializeProperties", MemberVisibility.InternalProtected, false).Static();
            methodClass.ReturnType = t;
            var p2 = Helper.Primitive("Created properties");
            var a2 = performanceCounterCategory.Method("Exists", Helper.Bool).Call(categ);
            var b2 = performanceCounterCategory.Method("CounterExists", Helper.Bool).Call(p2, categ);
            var Try2 = methodClass.Statements.Try();
            Try2.CatchClauses.Catch("e", new TypeExp(typeof(Exception)));
            var If21 = Try2.TryStatements.If(a2);
            var If22 = If21.TrueStatements.If(b2);
            If22.TrueStatements.Return(counterProperties.New(categ, p2, Helper.False));
            methodClass.Statements.Return(Helper.Null);

            
        }


        #endregion


        /// <summary>
        /// Generates the mapper.
        /// </summary>
        /// <param name="code">The code.</param>
        /// <param name="mapper">The mapper.</param>
        /// <param name="model">The model.</param>
        internal void GenerateMapper(CodeNamespace code, Mapper mapper)
        {

            Stack<RecurcifBag> root = new Stack<RecurcifBag>();            

            VariableExp Source = new VariableExp("source", mapper.MappingSource.TypeElement.AssemblyQualifiedName);
            VariableExp Target = new VariableExp("target", mapper.MappingDestination.TypeElement.AssemblyQualifiedName);

            if (mapper.MappingSource.TypeElement == null || mapper.MappingDestination.TypeElement == null)
                return;

            Dictionary<string, TypeExp> ListeConverters = new Dictionary<string, TypeExp>();

            CodeTypeDeclaration Class = CreateClass(mapper);
            code.Types.Add(Class);
            Class.Comments.Add(new CodeCommentStatement(new CodeComment(string.Format("<summary>Mapping's functions for {0} to {1} </summary>", Source.Type.TypeExpression.Name, Target.Type.TypeExpression.Name), true)));
                        
            var m1 = MethodMap1Parameter(Class, mapper, ListeConverters, Source, Target, root);
            m1.Comments.Add(new CodeCommentStatement(new CodeComment(string.Format("<summary>Map all the properties from {0} in a new instance of {1}</summary>", Source.VariableName, Target.Type.TypeExpression.Name), true)));
            m1.Comments.Add(new CodeCommentStatement(new CodeComment(string.Format(@"<param name=""{0}"">The {0}. If the source is null, the method throw a exception.</param>", Source.VariableName), true)));            

            var m2 = MethodMap2Parameter(Class, mapper, ListeConverters, Source, Target, root);
            m2.Comments.Add(new CodeCommentStatement(new CodeComment(string.Format("<summary>Map all the properties from {0} in target</summary>", Source.VariableName), true)));
            m2.Comments.Add(new CodeCommentStatement(new CodeComment(string.Format(@"<param name=""{0}"">The {0}. If the source is null, the method throw a exception.</param>", Source.VariableName), true)));
            m2.Comments.Add(new CodeCommentStatement(new CodeComment(string.Format(@"<param name=""{0}"">The value is created if null.</param>", Target.VariableName), true)));

            CreatefieldsConverter(Class, ListeConverters);

            // Enumerables
            TypeExpression typeRetour = TypeExpression.Analyse(typeof(ICollection<string>));
            typeRetour.SubType.TypeName = Target.Type.TypeExpression.AssemblyQualifiedName;
            VariableExp TargetEnumerable = new VariableExp(Target.VariableName, typeRetour);

            TypeExpression typeSource = TypeExpression.Analyse(typeof(IEnumerable<string>));
            typeSource.SubType.TypeName = Source.Type.TypeExpression.AssemblyQualifiedName;
            VariableExp SourceEnumerable = new VariableExp(Source.VariableName, typeSource);

            var m3 = MethodMap1ParameterIEnumerable(Class, mapper, ListeConverters, SourceEnumerable, TargetEnumerable, root);
            m3.Comments.Add(new CodeCommentStatement(new CodeComment(string.Format("<summary>Map all the item from the source list in a new instance of {0}</summary>", Target.Type.TypeExpression.Name), true)));
            m3.Comments.Add(new CodeCommentStatement(new CodeComment(string.Format(@"<param name=""{0}"">The {0}.If the source is null, the method throw a exception.</param>", Source.VariableName), true)));



        }

        private void CreatefieldsConverter(CodeTypeDeclaration Class, Dictionary<string, TypeExp> ListeConverters)
        {            
            foreach (var item in ListeConverters)
            {
                string n = "_C" + item.Value.TypeExpression.Name.Replace(".", "");
                Class.Members.CreateFieldAutoInit(n, MemberVisibility.Private, false, item.Value);
            }
        }



        #region Methodes

        private MethodMember MethodMapNoParameter(Mapper mapper, VariableExp Source, VariableExp Target, bool returnTargetIfNull, bool isAbstract)
        {

            _cnt = 0;

            ParameterExp p1 = null;
            p1 = new ParameterExp(Source.Type, Source.VariableName);

            MethodMember method = Helper.CreateMethod(
                mapper.MapMethodName, MemberVisibility.Public, 
                true,
                Target.Type,
                p1
                );

            if (!mapper.MappingSource.TypeElement.IsValueType || isAbstract)
            {
                if (!returnTargetIfNull)
                    method.Statements.If(Source.IsNull, mapper.MappingSource.TypeElement.IsValueType || isAbstract ? Helper.ReturnNull() : new CodeMethodReturnStatement(Target.New()));
                else
                {
                    var blockIf = method.Statements.If(Source.IsNull);
                    blockIf.TrueStatements.Return(Target);
                }
            }

            if (Model.DebuggerStepThrough)            
                method.CreateAttribute(typeof(System.Diagnostics.DebuggerStepThroughAttribute));
                       
            return method;
        }

        private CodeMemberMethod MethodMap1Parameter(CodeTypeDeclaration Class, Mapper mapper, Dictionary<string, TypeExp> ListeConverters, VariableExp Source, VariableExp Target, Stack<RecurcifBag> queue)
        {

            var t = mapper.MappingDestination.TypeElement;
            var a = t.IsAbstract || t.IsInterface;

            _cnt = 0;
            MethodMember method = MethodMapNoParameter(mapper, Source, Target, false, a);

            if (mapper.MappingDestination.TypeElement.IsValueType)
                method.Statements.Declare(Target);
            else
                method.Statements.Declare(Target, Helper.Null);


            Class.Members.Add(method);

            CodeExpression expr = CreateTarget(mapper, Source, Target);
            string nameType = BuildIndexKey(Source.Type.TypeExpression, Target.Type.TypeExpression);
            if (IndexexDic.ContainsKey(nameType))
            {

              

                IndexBag index = IndexexDic[nameType];
                CodeExpression Source2 = null;
                if (mapper.IndexProperty == "Instance")
                    Source2 = Source;
                else
                    Source2 = Source.Property(mapper.IndexProperty, typeof(object));

                var If = method.Statements.If(index.Variable.Method("ContainsKey", Helper.Bool).Call(Source2));
                var s = Helper.Cast(index.Variable.Index(Source2), Target.Type);
                If.TrueStatements.Return(s);

                CodeTryCatchFinallyStatement lock1 = If.FalseStatements.Lock(index.Variable.Property("SyncRoot", typeof(object)));
                var If2 = lock1.TryStatements.If(index.Variable.Method("ContainsKey", Helper.Bool).Call(Source2));
                If2.TrueStatements.Return(s);

                If2.FalseStatements.Set(Target, expr);
                If2.FalseStatements.Add(index.Variable.Method("Add").Call(Source2, Target));

             

            }
            else
            {

                if (!mapper.MappingDestination.TypeElement.IsValueType)
                    method.Statements.Set(Target, expr);

                if (mapper.MappingModel.Instrumentation)
                {
                    var If3 = method.Statements.If(Helper.IsNotNull(PerCounterClass));
                    If3.TrueStatements.Add(PerCounterIncrementClass);
                }
            }

            CreateMap(method.Statements, mapper, ListeConverters, Source, Target, queue);

            if (mapper.MappingModel.Instrumentation)
            {
                var If4 = method.Statements.If(Helper.IsNotNull(PerCounterClass));
                If4.TrueStatements.Add(PerCounterDecrementClass);               
            }
            

            method.Statements.Return(Target);
            return method;
        }

        private CodeMemberMethod MethodMap2Parameter(CodeTypeDeclaration Class, Mapper mapper, Dictionary<string, TypeExp> ListeConverters, VariableExp Source, VariableExp Target, Stack<RecurcifBag> queue)
        {

            _cnt = 0;

            var t = mapper.MappingDestination.TypeElement;
            var a = t.IsAbstract || t.IsInterface;

            CodeMemberMethod method = MethodMapNoParameter(mapper, Source, Target, true, a);
            method.Parameters.Add((ParameterExp)Target);
            Class.Members.Add(method);

            CodeExpression expr = CreateTarget(mapper, Source, Target);
            CodeStatementCollection Cde = null;


            if (!mapper.MappingDestination.TypeElement.IsValueType)
            {
                var If = method.Statements.If(Helper.IsNull(Target));
                Cde = If.TrueStatements;
            }
            else            
                Cde = method.Statements;


            string nameType = BuildIndexKey(Source.Type.TypeExpression, Target.Type.TypeExpression);
            if (IndexexDic.ContainsKey(nameType))
            {
                IndexBag index = IndexexDic[nameType];

                CodeExpression Source2 = null;
                if (mapper.IndexProperty == "Instance")
                    Source2 = Source;
                else
                    Source2 = Source.Property(mapper.IndexProperty, typeof(object));

                var If1 = Cde.If(index.Variable.Method("ContainsKey", Helper.Bool).Call(Source2));
                var s = Helper.Cast(index.Variable.Index(Source2), Target.Type);
                If1.TrueStatements.Set(Target, s);

                CodeTryCatchFinallyStatement lock1 = If1.FalseStatements.Lock(index.Variable.Property("SyncRoot", typeof(object)));
                var If2 = lock1.TryStatements.If(index.Variable.Method("ContainsKey", Helper.Bool).Call(Source2));
                If2.TrueStatements.Set(Target, s);
                If2.FalseStatements.Set(Target, expr);
                If2.FalseStatements.Add(index.Variable.Method("Add").Call(Source2, Target));
  
            }
            else
            {                
                if (Model.Instrumentation)
                {
                    var If2 = Cde.If(Helper.IsNotNull(PerCounterClass));
                    If2.TrueStatements.Add(PerCounterIncrementClass);
                }

                Cde.Set(Target, expr);

            }

            CreateMap(method.Statements, mapper, ListeConverters, Source, Target, queue);

            if (Model.Instrumentation)
            {
                var If2 = Cde.If(Helper.IsNotNull(PerCounterClass));
                If2.TrueStatements.Add(PerCounterDecrementClass);
            }
            

            method.Statements.Return(Target);
            return method;
        }

        private CodeMemberMethod MethodMap1ParameterIEnumerable(CodeTypeDeclaration Class, Mapper mapper, Dictionary<string, TypeExp> ListeConverters, VariableExp Source, VariableExp Target, Stack<RecurcifBag> queue)
        {

            _cnt = 0;
            MethodMember method = MethodMapNoParameter(mapper, Source, Target, false, true);
            var _created = new VariableExp("created", typeof(bool));

            Class.Members.Add(method);

            var expr = CreateEnumerable(mapper, Target);
            method.Statements.Declare(Target, expr);

            #region Instrumentation
            if (mapper.MappingModel.Instrumentation)
            {
                var If2 = method.Statements.If(Helper.IsNotNull(PerCounterClass));
                If2.TrueStatements.Add(PerCounterIncrementClass);
            }
            #endregion

            VariableExp current = null;

            TypeExpression myTypeEnum = GetSubType(Source.Type.TypeExpression, mapper.MappingSource.TypeElement.ItemType);

            //foreach (T current in source.PropertyName)
            var f2 = method.Statements.ForEach(myTypeEnum, Source, out current, "_cur" + myTypeEnum.Name + _cnt.ToString(), "enumerator" + _cnt.ToString());
            var f1 = f2.Statements.If(Helper.IsNotNull(current));

            VariableExp targetItem = new VariableExp("targetItem" + _cnt.ToString(), Target.Type.TypeExpression.SubType);
            var expr2 = CreateTarget(mapper, current, targetItem);


            string nameType = BuildIndexKey(Source.Type.TypeExpression, Target.Type.TypeExpression);
            if (IndexexDic.ContainsKey(nameType))
            {

                f1.TrueStatements.Declare(targetItem, Helper.Null);

                IndexBag index = IndexexDic[nameType];

                CodeExpression Source2 = null;
                if (mapper.IndexProperty == "Instance")
                    Source2 = current;
                else
                    Source2 = current.Property(mapper.IndexProperty, typeof(object));

                // lock
                var If = f1.TrueStatements.If(index.Variable.Method("ContainsKey", Helper.Bool).Call(current));
                var s = Helper.Cast(index.Variable.Index(Source2), targetItem.Type);
                If.TrueStatements.Set(targetItem, s);
                If.TrueStatements.Add(Target.Method("Add").Call(targetItem));


                CodeTryCatchFinallyStatement lock1 = If.FalseStatements.Lock(IndexexDic[nameType].Variable.Property("SyncRoot", typeof(object)));
                var If2 = lock1.TryStatements.If(index.Variable.Method("ContainsKey", Helper.Bool).Call(current));
                If2.TrueStatements.Set(targetItem, s);
                If2.TrueStatements.Add(Target.Method("Add").Call(targetItem));
     

                If2.FalseStatements.Set(targetItem, expr2);
                If2.FalseStatements.Add(index.Variable.Method("Add").Call(Source2, targetItem));
                If2.FalseStatements.Add(Target.Method("Add").Call(targetItem));

                f2.Statements.Declare(0, _created, new CodePrimitiveExpression(false));
                If2.FalseStatements.Set(_created, new CodePrimitiveExpression(true));

                var If3 = f1.TrueStatements.If(_created);
                CreateMap(If3.TrueStatements, mapper, ListeConverters, current, targetItem, queue);

            }
            else
            {

                f1.TrueStatements.Declare(targetItem, expr2);
                f1.TrueStatements.Add(Target.Method("Add").Call(targetItem));
                
                #region Instrumentation
                if (Model.Instrumentation)
                {
                    var If3 = method.Statements.If(Helper.IsNotNull(PerCounterClass));
                    If3.TrueStatements.Add(PerCounterIncrementClass);
                }
                #endregion

                CreateMap(f1.TrueStatements, mapper, ListeConverters, current, targetItem, queue);

            }


            #region Instrumentation
            if (Model.Instrumentation)
            {
                var If6 = method.Statements.If(Helper.IsNotNull(PerCounterClass));
                If6.TrueStatements.Add(PerCounterDecrementClass);
            }
            #endregion

            method.Statements.Return(Target);
            return method;
        }

        #endregion

        /// <summary>
        /// entree des conversions
        /// </summary>
        /// <param name="method"></param>
        /// <param name="mapper"></param>
        /// <param name="ListeConverters"></param>
        /// <param name="model"></param>
        private void CreateMap(CodeStatementCollection method, Mapper mapper, Dictionary<string, TypeExp> ListeConverters, VariableExp Source, VariableExp Target, Stack<RecurcifBag> queue)
        {

            queue.Push(new RecurcifBag() { Mapper = mapper, Source = Source, Count = 0 });

            _cnt++;

            if (mapper.ExecuteMethodBeforeCast)
            {
                CodeExpression cc = Source.root ?? Source;
                method.Add(MyThis.Method("ExecuteBeforeMapping", TypeExp.Void).Call(cc, Target));
            }

            int cntprop = mapper.ItemMaps.Where(c => c.Enable).Count();

            if (Model.Instrumentation)
            {
                var If2 = method.If(Helper.IsNotNull(PerCounterProperties));
                If2.TrueStatements.Add(PerCounterIncrementProperties.Call(Helper.Primitive(cntprop)));
            }

            CreateMapProperties(method, mapper, ListeConverters, Source, Target, queue);

            if (Model.Instrumentation)
            {
                var If3 = method.If(Helper.IsNotNull(PerCounterProperties));
                If3.TrueStatements.Set(PerCounterDecrementProperties, Helper.Subtract(PerCounterDecrementProperties, Helper.Primitive(cntprop)));      
            }

            if (mapper.MappingSource.TypeElement.IsEnumerable)
                CreateEnumerator(method, mapper, Source, Target);

            if (mapper.ExecuteMethodAfterCast)
            {
                CodeExpression cc = Source.root ?? Source;
                method.Add(MyThis.Method("ExecuteAfterMapping", TypeExp.Void).Call(cc, Target));
            }

            RecurcifBag bag = queue.Pop();

        }

        private void CreateMapProperties(CodeStatementCollection method, Mapper mapper, Dictionary<string, TypeExp> ListeConverters, VariableExp Source, VariableExp Target, Stack<RecurcifBag> queue)
        {

            //Type SourceType = Source.Type.TypeExpression.Evaluate(FindType);
            //Type DestinationType = Target.Type.TypeExpression.Evaluate(FindType);

            //// valeur simples comme enum, int, long, string Ect ...
            //if (IsSimpleValue(SourceType) && IsSimpleValue(DestinationType))
            //    if (SourceType == DestinationType)
            //    {
            //        method.Set(Target, Source);
            //        return;
            //    }

            #region map properties

            foreach (ItemMap item in mapper.ItemMaps)
            {

                PropertyExp SourceProperty = Source.Property(item.SourceName, item.SourceType);
                PropertyExp TargetProperty = Target.Property(item.TargetName
                    , string.IsNullOrEmpty(item.TargetAs)
                        ? item.TargetType
                        : item.TargetAs
                    );

                if (item.Enable)
                {

                    CodeStatementCollection method2 = null;
                    if (UseContitionnalMap(item) && !item.SourceName.Contains("."))
                    {
                        var If1 = method.If(new MethodExp(Helper.This(), item.ConditionnalMappingMethod.Trim()).Call(Source));
                        method2 = If1.TrueStatements;
                    }
                    else
                        method2 = method;

                    switch (item.TypeCast)
                    {

                        case CastType.Array:
                        case CastType.ObservableCollection:
                        case CastType.List:
                        case CastType.Collection:
                        case CastType.Hashset:
                            method.Add(new CodeCommentStatement(new CodeComment(string.Format(" ---(Model {1})------------- Copy Liste {0} ------------------------", SourceProperty.Type.TypeExpression.CSharp, item.TypeCast))));
                            CreateList(method2, item, Source, Target, SourceProperty, TargetProperty, ListeConverters, queue);
                            method.Add(new CodeCommentStatement(new CodeComment(string.Format(" ---------------- End Copy Liste {0} ---------------------", SourceProperty.Type.TypeExpression.CSharp, item.TypeCast))));                           
                            break;

                        case CastType.Map:
                            if (string.IsNullOrEmpty(item.ExternalModelDocument))
                            {
                                method.Add(new CodeCommentStatement(new CodeComment(string.Format("---(Model {2})----------------- Start {0} ({1})--------------------", mapper.Name, SourceProperty.Type.TypeExpression.Name, item.TypeCast))));
                                CreateConverterMapClass(method2, item, Source, Target, SourceProperty, TargetProperty, ListeConverters, queue);
                                method.Add(new CodeCommentStatement(new CodeComment(string.Format("-------------------- End {0} ({1})----------------------", mapper.Name, SourceProperty.Type.TypeExpression.Name, item.TypeCast))));
                            }
                            else
                            {
                                method.Add(new CodeCommentStatement(new CodeComment(string.Format("---(Model {1})----------------- Start {0} ({1})--------------------", item.ExternalModelDocument, SourceProperty.Type.TypeExpression.Name, item.TypeCast))));
                                CreateConverterExternalMapClass(method2, item, Source, Target, SourceProperty, TargetProperty, ListeConverters, queue);
                                method.Add(new CodeCommentStatement(new CodeComment(string.Format("-------------------- End {0} ({1})----------------------", item.ExternalModelDocument, SourceProperty.Type.TypeExpression.Name, item.TypeCast))));
                            }
                            break;

                        #region Converter
                        
                        case CastType.Implicit:
                        case CastType.None:
                            CreateSimpleCopy(method2, SourceProperty, TargetProperty, item);
                            break;

                        case CastType.EnumByValue:
                            CreateEnumByValue(method2, SourceProperty, TargetProperty, item);
                            break;

                        case CastType.EnumByLabel:
                            CreateEnumByLabel(method2, SourceProperty, TargetProperty, item);
                            break;

                        case CastType.TypeConverter:
                            TypeExp c = CreateTypeConverterMapProperty(method2, item, Source, Target, SourceProperty, TargetProperty);
                            if (!ListeConverters.ContainsKey(c.TypeExpression.FullName))
                                ListeConverters.Add(c.TypeExpression.FullName, c);
                            break;

                        case CastType.IValueConverter:
                            TypeExp d = CreateIValueConverterMapProperty(method2, item, Source, Target, SourceProperty, TargetProperty);
                            if (!ListeConverters.ContainsKey(d.TypeExpression.FullName))
                                ListeConverters.Add(d.TypeExpression.FullName, d);
                            break;

                        case CastType.IConverter:
                            TypeExp c1 = CreateIConverterMapProperty(method2, item, Source, Target, SourceProperty, TargetProperty);
                            if (!ListeConverters.ContainsKey(c1.TypeExpression.FullName))
                                ListeConverters.Add(c1.TypeExpression.FullName, c1);
                            break;

                        case CastType.Explicit:
                            CreateExplicitProperty(method2, item, Source, Target, SourceProperty, TargetProperty);
                            break;

                        case CastType.Custom:
                            CreateCustomMapProperty(method2, item, Source, Target, SourceProperty, TargetProperty);
                            break;

                        #endregion

                    }
                }
            }

            #endregion

        }

        private static bool UseContitionnalMap(ItemMap item)
        {

            if (!string.IsNullOrEmpty(item.ConditionnalMappingMethod.Trim()))
                switch (item.TypeCast)
                {
                    case CastType.ObservableCollection:
                    case CastType.List:
                    case CastType.Collection:
                    case CastType.Hashset:
                        return false;
                    default:
                        return true;
                }
           
            return false;

        }

        private static void CreateSimpleCopy(CodeStatementCollection methode, PropertyExp SourceProperty, PropertyExp TargetProperty, ItemMap map)
        {

            CodeStatementCollection method = methode;

            if (map.SourceIsClass && !(SourceProperty.Type.TypeExpression.FullName == typeof(string).FullName))
            {
                if (!IsSimpleValue(SourceProperty))
                {
                    var if1 = method.If(Helper.IsNotNull(SourceProperty));
                    method = if1.TrueStatements;
                }
            }
            else if (SourceProperty.PropertyName.Contains("."))
            {

                CodeExpression source = SourceProperty.TargetObject;
                CodeConditionStatement if2 = null;
                string[] tb = SourceProperty.PropertyName.Split('.');
                for (int i = 0; i < tb.Length - 1; i++)
                {
                    source = new PropertyExp(source, tb[i], typeof(object));
                    if2 = method.If(Helper.IsNotNull(source));
                    method = if2.TrueStatements;
                }

            }


            System.CodeDom.CodeExpression s = null;

            if (map.SourceIsNullable && !map.TargetIsNullable)
                s = SourceProperty.Method("GetValueOrDefault").Call() as System.CodeDom.CodeExpression;

            // cas particuliers
            else if (!TryGetSpecificCast(SourceProperty, TargetProperty, map, out s))
                s = SourceProperty;

            method.Set(TargetProperty, s);

        }

        private static void CreateEnumByLabel(CodeStatementCollection methode, PropertyExp SourceProperty, PropertyExp TargetProperty, ItemMap map)
        {            

            CodeStatementCollection method = methode;

            if (SourceProperty.PropertyName.Contains("."))
            {

                CodeExpression source = SourceProperty.TargetObject;
                CodeConditionStatement if2 = null;
                string[] tb = SourceProperty.PropertyName.Split('.');
                for (int i = 0; i < tb.Length - 1; i++)
                {
                    source = new PropertyExp(source, tb[i], typeof(object));
                    if2 = method.If(Helper.IsNotNull(source));
                    method = if2.TrueStatements;
                }

            }


            #region Switch Enum
            
            CodeStatementCollection c1 = method;
            var enumTarget = SerialiserBase.UnSerialize(map.TargetProperties) as EnumSerializer;
            var enumSource = SerialiserBase.UnSerialize(map.SourceProperties) as EnumSerializer;

            foreach (var item in enumSource.Items)            
            {
                if (enumTarget.Items.FirstOrDefault(c => c.Name == item.Name) != null)
                {

                    var m1 = new CodeMethodInvokeExpression() { Method = new CodeMethodReferenceExpression(SourceProperty, "Equals") };

                    CodeTypeReferenceExpression s1 = 
                        string.IsNullOrEmpty(enumSource.DeclaringType)
                            ? new CodeTypeReferenceExpression(enumSource.Namespace + "." + enumSource.Name)
                            : new CodeTypeReferenceExpression(enumSource.Namespace  + "." + enumSource.DeclaringType + "." + enumSource.Name);
                    var field1 = new CodeFieldReferenceExpression(s1, item.Name);
                    m1.Parameters.Add(field1);

                    var c = new CodeConditionStatement() { Condition = m1 };

                    CodeTypeReferenceExpression t1 =
                        string.IsNullOrEmpty(enumTarget.DeclaringType)
                            ? new CodeTypeReferenceExpression(enumTarget.Namespace + "." + enumTarget.Name)
                            : new CodeTypeReferenceExpression(enumTarget.Namespace + "." + enumTarget.DeclaringType + "." + enumTarget.Name);

                    c.TrueStatements.Set(TargetProperty, new CodeFieldReferenceExpression(t1, item.Name));
                    c1.Add(c);

                    c1 = c.FalseStatements;
                }
            }

            #endregion

            // old version
            //CodeExpression _source = SourceProperty;
            //if (map.SourceIsEnum)
            //    _source = SourceProperty.Method("ToString").Call();
            //var meth = Helper.Cast(new MethodExp(new CodeVariableReferenceExpression("System.Enum"), "Parse").Call(TargetProperty.Type.TypeOf(), _source), TargetProperty.Type);
            //method.Set(TargetProperty, meth);

        }

        private static void CreateEnumByValue(CodeStatementCollection methode, PropertyExp SourceProperty, PropertyExp TargetProperty, ItemMap map)
        {

            CodeStatementCollection method = methode;

            if (SourceProperty.PropertyName.Contains("."))
            {

                CodeExpression source = SourceProperty.TargetObject;
                CodeConditionStatement if2 = null;
                string[] tb = SourceProperty.PropertyName.Split('.');
                for (int i = 0; i < tb.Length - 1; i++)
                {
                    source = new PropertyExp(source, tb[i], typeof(object));
                    if2 = method.If(Helper.IsNotNull(source));
                    method = if2.TrueStatements;
                }

            }

            method.Set(TargetProperty, SourceProperty);

        }

        private static bool TryGetSpecificCast(PropertyExp SourceProperty, PropertyExp TargetProperty, ItemMap map, out CodeExpression t)
        {

            string source = SourceProperty.Type.TypeExpression.FullName;
            string target = TargetProperty.Type.TypeExpression.FullName;

            t = null;
            if (source == typeof(Guid).FullName && target == typeof(byte[]).FullName)
            {
                t = SourceProperty.Method("ToByteArray").Call();
                return true;
            }

            else if ((source == typeof(string).FullName || source == typeof(byte[]).FullName) && target == typeof(Guid).FullName)
            {
                TypeExp r = new TypeExp(typeof(Guid));
                t = r.New(SourceProperty);
                return true;
            }

            else if (target == "System.String")
            {
                if (map.SourceIsEnum || MappingHelper.tbTypeToString.Contains(source))
                {
                    t = SourceProperty.Method("ToString").Call();
                    return true;
                }
                else if (map.SourceIsNullable)
                {
                    t = SourceProperty.Method("GetValueOrDefault").Call().Method("ToString").Call();
                    return true;
                }
            }

            var castItem = MappingHelper.GetCastExplicit(source, target);
            if (castItem != null)
            {
                VariableExp v1 = new VariableExp(castItem[0], typeof(object));
                t = v1.Method(castItem[1]).Call(SourceProperty);
                return true;
            }


            return false;


        }   

        private void CreateList(CodeStatementCollection method, ItemMap item, VariableExp source, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty, Dictionary<string, TypeExp> ListeConverters, Stack<RecurcifBag> queue)
        {

            TypeExpression t = null;

            if (string.IsNullOrEmpty(item.TargetAs))
            {

                if (item.TypeCast == CastType.Collection)
                    t = targetProperty.Type.TypeExpression.Translate(typeof(Collection<>));

                else if (item.TypeCast == CastType.List || item.TypeCast == CastType.Array)
                {

                    if (targetProperty.Type.TypeExpression.IsArray)
                    {

                        var u = targetProperty.Type.TypeExpression.AssemblyQualifiedName;
                        u = u.Replace("[]", "");

                        t = TypeExpression.Analyse(typeof(System.Collections.Generic.List<>).AssemblyQualifiedName);
                        t.Liste.Add(TypeExpression.Analyse(u));

                    }
                    else
                    {
                        t = targetProperty.Type.TypeExpression.Translate(typeof(System.Collections.Generic.List<>));
                    }

                }

                else if (item.TypeCast == CastType.ObservableCollection)
                    t = targetProperty.Type.TypeExpression.Translate(typeof(ObservableCollection<>));

                else if (item.TypeCast == CastType.Hashset)
                    t = targetProperty.Type.TypeExpression.Translate(typeof(HashSet<>));

            }
            else
            {
                t = TypeExpression.Analyse(item.TargetAs);
            }

            


            VariableExp liste = new VariableExp("liste" + _cnt.ToString(), t);


            if (string.IsNullOrEmpty(item.ExternalModelDocument))
            {

                #region Get internal Mapper

                // cherche un mapper qui corresponde aux type enumérés.
                Mapper mapper2 = item.Mapper.MappingModel.FindMapperFor(item);

                if (mapper2 != null)
                {

                    if (item.TypeCast == CastType.Array)
                        CopyArrayMapper(method, item, target, sourceProperty, targetProperty, ListeConverters, queue, liste, mapper2);
                    else
                        CopyListeMapper(method, item, target, sourceProperty, targetProperty, ListeConverters, queue, liste, mapper2);

                }
                // aucun mapper n'existe pour ce cas, on essaie les types de bases
                else
                {

                    if (item.TypeCast == CastType.Array && item.SourceType == item.TargetType && item.SourceIsArray)
                    {

                        method.SetNewArray(targetProperty, sourceProperty.Property("Length"));
                        method.Add(
                            Helper.MethodStatic(typeof(System.Array), "Copy")
                            .Call
                            (
                              sourceProperty, 
                              targetProperty, 
                              sourceProperty.Property("Length")
                            )
                        );


                    }
                    else
                    {
                        var ss1 = sourceProperty.Type.TypeExpression.IsGeneric
                            ? sourceProperty.Type.TypeExpression.SubType.TypeName
                            : sourceProperty.Type.TypeExpression.TypeName;

                        var tt1 = targetProperty.Type.TypeExpression.IsGeneric
                            ? targetProperty.Type.TypeExpression.SubType.TypeName
                            : targetProperty.Type.TypeExpression.TypeName;

                        Type TypeSource = TypeExpression.Analyse(ss1).Evaluate(FindType);
                        Type TypeDestination = TypeExpression.Analyse(tt1).Evaluate(FindType);

                        if (null != TypeSource && null != TypeDestination)
                            CopyListeReflexion(method, item, target, sourceProperty, targetProperty, ListeConverters, queue, liste, TypeSource, TypeDestination);

                        else
                            method.Add(new CodeCommentStatement(new CodeComment("Cannot find the good mapper")));
                    }
                }

                #endregion

            }
            else
            {

                #region Get External Mapper

                // cherche un mapper qui corresponde aux type enumérés.
                Mapper mapper2 = item.Mapper.MappingModel.FindExternalMapperFor(item.ExternalModelDocument);

                if (mapper2 != null)
                {

                    if (item.TypeCast == CastType.Array)
                        CopyArrayExternalMapper(method, item, target, sourceProperty, targetProperty, ListeConverters, queue, liste, mapper2);
                    else
                        CopyListeExternalMapper(method, item, target, sourceProperty, targetProperty, ListeConverters, queue, liste, mapper2);

                }

                #endregion

            }
   
        }


        public TypeExpression GetSubType(TypeExpression type, string item)
        {
            TypeExpression t1 = type.SubType;

            if (t1 == null)
                t1 = TypeExpression.Analyse(item);

            return t1;

        }


        private void CopyArrayMapper(CodeStatementCollection method, ItemMap item, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty, Dictionary<string, TypeExp> ListeConverters, Stack<RecurcifBag> queue, VariableExp liste, Mapper mapper)
        {
            // Process Mapper

            TypeExpression myTypeEnum = GetSubType(sourceProperty.Type.TypeExpression, item.SourceItemType);
            TypeExpression subtype = GetSubType(targetProperty.Type.TypeExpression, item.TargetItemType);

            var methode = method.If(Helper.IsNotNull(sourceProperty));

            methode.TrueStatements.Declare(liste, liste.New());

            if (Model.Instrumentation)
            {
                var If3 = methode.TrueStatements.If(Helper.IsNotNull(PerCounterClass));
                If3.TrueStatements.Add(PerCounterIncrementClass);
            }

            VariableExp current = null;

            //foreach (T current in source.PropertyName)
            var f2 = methode.TrueStatements.ForEach(myTypeEnum, sourceProperty, out current, "_cur" + myTypeEnum.Name + _cnt.ToString(), "enumerator" + _cnt.ToString());

            CodeStatementCollection code = f2.Statements;
            if (!IsSimpleValue(subtype))
            {
                var If11 = f2.Statements.If(Helper.IsNotNull(current));
                code = If11.TrueStatements;
            }

            // copy conditionnelle
            if (!string.IsNullOrEmpty(item.ConditionnalMappingMethod.Trim()))
            {
                CodeConditionStatement f1 = null;
                f1 = code.If(new MethodExp(Helper.This(), item.ConditionnalMappingMethod.Trim()).Call(current));
                code = f1.TrueStatements;
            }

            VariableExp target2 = code.Declare(target.VariableName + _cnt.ToString(), GetSubType(targetProperty.Type.TypeExpression, item.TargetItemType));
            bool created = false;
            CodeExpression expr = CreateTarget(mapper, current, target2);

            string nameType = BuildIndexKey(current.Type.TypeExpression, subtype);
            if (IndexexDic.ContainsKey(nameType))
            {
     
                code.Add(new CodeCommentStatement(new CodeComment(string.Format("HACK: Recurcif code detected {0}", ShowQueue(queue)))));

                CodeExpression value = null;
                if (current.Type.TypeExpression.Equals(myTypeEnum))
                    value = MyThis.Method(mapper.MapMethodName, targetProperty.Type).Call(current);
                else
                    value = MyThis.Method(mapper.MapMethodName, targetProperty.Type).Call(current).As(targetProperty.Type);

                code.Set(target2, value);
     
            }
            else
            {



                code.Set(target2, expr);
                if (Model.Instrumentation)
                {
                    var If4 = code.If(Helper.IsNotNull(PerCounterClass));
                    If4.TrueStatements.Add(PerCounterIncrementClass);
                }

                created = true;
                CreateMap(code, mapper, ListeConverters, current, target2, queue);

            }

            code.Add(liste.Method("Add").Call(target2));

            if (Model.Instrumentation && created)
            {
                var If5 = methode.TrueStatements.If(Helper.IsNotNull(PerCounterClass));
                If5.TrueStatements.Add(PerCounterDecrementClass);
                var If6 = code.If(Helper.IsNotNull(PerCounterClass));
                If6.TrueStatements.Add(PerCounterDecrementClass);
            }

            methode.TrueStatements.Set(targetProperty, liste.Method("ToArray").Call());

        }

        private void CopyArrayExternalMapper(CodeStatementCollection method, ItemMap item, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty, Dictionary<string, TypeExp> ListeConverters, Stack<RecurcifBag> queue, VariableExp liste, Mapper mapper)
        {
            // Process Mapper

            TypeExpression myTypeEnum = GetSubType(sourceProperty.Type.TypeExpression, item.SourceItemType);
            TypeExpression subtype = GetSubType(targetProperty.Type.TypeExpression, item.TargetItemType);

            var methode = method.If(Helper.IsNotNull(sourceProperty));

            methode.TrueStatements.Declare(liste, liste.New());

            if (Model.Instrumentation)
            {
                var If3 = methode.TrueStatements.If(Helper.IsNotNull(PerCounterClass));
                If3.TrueStatements.Add(PerCounterIncrementClass);
            }

            VariableExp current = null;

            //foreach (T current in source.PropertyName)
            var f2 = methode.TrueStatements.ForEach(myTypeEnum, sourceProperty, out current, "_cur" + myTypeEnum.Name + _cnt.ToString(), "enumerator" + _cnt.ToString());

            CodeStatementCollection code = f2.Statements;
            if (!IsSimpleValue(subtype))
            {
                var If11 = f2.Statements.If(Helper.IsNotNull(current));
                code = If11.TrueStatements;
            }

            // copy conditionnelle
            if (!string.IsNullOrEmpty(item.ConditionnalMappingMethod.Trim()))
            {
                CodeConditionStatement f1 = null;
                f1 = code.If(new MethodExp(Helper.This(), item.ConditionnalMappingMethod.Trim()).Call(current));
                code = f1.TrueStatements;
            }


            VariableExp target2 = code.Declare(target.VariableName + _cnt.ToString(), GetSubType(targetProperty.Type.TypeExpression, item.TargetItemType));
            bool created = false;
            CodeExpression expr = CreateTarget(mapper, current, target2);

            code.Add(new CodeCommentStatement(new CodeComment(string.Format("-------------------- External call {0} ({1})--------------------", mapper.Name, sourceProperty.Type.TypeExpression.Name))));
            MethodExp m = GetVariableMapperFor(item.ExternalModelDocument, mapper);
            CodeExpression value = null;
            if (current.Type.TypeExpression.Equals(myTypeEnum))
                value = m.Call(current);
            else
                value = m.Call(current).As(targetProperty.Type);
            code.Set(target2, value);
            code.Add(liste.Method("Add").Call(target2));

            if (Model.Instrumentation && created)
            {
                var If5 = methode.TrueStatements.If(Helper.IsNotNull(PerCounterClass));
                If5.TrueStatements.Add(PerCounterDecrementClass);
                var If6 = code.If(Helper.IsNotNull(PerCounterClass));
                If6.TrueStatements.Add(PerCounterDecrementClass);
            }

            methode.TrueStatements.Set(targetProperty, liste.Method("ToArray").Call());

        }

        private void CopyListeMapper(CodeStatementCollection method, ItemMap item, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty, Dictionary<string, TypeExp> ListeConverters, Stack<RecurcifBag> queue, VariableExp liste, Mapper mapper)
        {

            var methode = method.If(Helper.IsNotNull(sourceProperty));


            if (item.CreateTarget)
            {
                if (liste.Type.Equals(targetProperty.Type))
                    methode.TrueStatements.Set(targetProperty, liste.New());
                else
                    methode.TrueStatements.Set(targetProperty, new CodeCastExpression(targetProperty.Type, liste.New()));
            }

            if (Model.Instrumentation)
            {
                var If3 = methode.TrueStatements.If(Helper.IsNotNull(PerCounterClass));
                If3.TrueStatements.Add(PerCounterIncrementClass);
            }

            VariableExp current = null;

            TypeExpression myTypeEnum = GetSubType(sourceProperty.Type.TypeExpression, item.SourceItemType);
            TypeExpression myTypeEnum2 = GetSubType(targetProperty.Type.TypeExpression, item.SourceItemType);


            //foreach (T current in source.PropertyName)
            var f2 = methode.TrueStatements.ForEach(myTypeEnum, sourceProperty, out current, "_cur" + myTypeEnum.Name + _cnt.ToString(), "enumerator" + _cnt.ToString());

            CodeStatementCollection code = f2.Statements;
            if (!IsSimpleValue(myTypeEnum2))
            {
                var If11 = f2.Statements.If(Helper.IsNotNull(current));
                code = If11.TrueStatements;
            }

            // copy conditionnelle
            if (!string.IsNullOrEmpty(item.ConditionnalMappingMethod.Trim()))
            {
                CodeConditionStatement f1 = null;
            
                f1 = code.If(new MethodExp(Helper.This(), item.ConditionnalMappingMethod.Trim()).Call(current));
                code = f1.TrueStatements;
            }

            
            bool created = false;

            VariableExp target2;
            var subtype = GetSubType(targetProperty.Type.TypeExpression, item.TargetItemType);
            string nameType = BuildIndexKey(current.Type.TypeExpression, subtype);

            if (IndexexDic.ContainsKey(nameType))
            {



                code.Add(new CodeCommentStatement(new CodeComment(string.Format("HACK: Recurcif code detected {0}", ShowQueue(queue)))));

                CodeExpression value = null;
                if (current.Type.TypeExpression.Equals(myTypeEnum))
                    value = MyThis.Method(mapper.MapMethodName, targetProperty.Type).Call(current);
                else
                    value = MyThis.Method(mapper.MapMethodName, targetProperty.Type).Call(current).As(targetProperty.Type);

                code.Add(targetProperty.Method("Add").Call(value));

            }
            else
            {

                target2 = code.Declare(target.VariableName + _cnt.ToString(), GetSubType(targetProperty.Type.TypeExpression, item.TargetItemType));
                CodeExpression expr = CreateTarget(mapper, current, target2);

                code.Set(target2, expr);
                if (Model.Instrumentation)
                {
                    var If4 = code.If(Helper.IsNotNull(PerCounterClass));
                    If4.TrueStatements.Add(PerCounterIncrementClass);
                }

                created = true;

                CreateMap(code, mapper, ListeConverters, current, target2, queue);

                if (targetProperty.Type.TypeExpression.Equals(liste.Type.TypeExpression))
                    code.Add(targetProperty.Method("Add").Call(target2));
                else
                    code.Add(targetProperty.As(liste.Type).Method("Add", liste.Type).Call(target2));
            }

            if (Model.Instrumentation && created)
            {

                var If5 = methode.TrueStatements.If(Helper.IsNotNull(PerCounterClass));
                If5.TrueStatements.Add(PerCounterDecrementClass);

                var If6 = code.If(Helper.IsNotNull(PerCounterClass));
                If6.TrueStatements.Add(PerCounterDecrementClass);

            }

    
        }

        private void CopyListeExternalMapper(CodeStatementCollection method, ItemMap item, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty, Dictionary<string, TypeExp> ListeConverters, Stack<RecurcifBag> queue, VariableExp liste, Mapper mapper)
        {
            // Process Mapper

            var methode = method.If(Helper.IsNotNull(sourceProperty));

            if (liste.Type.Equals(targetProperty.Type))
                methode.TrueStatements.Set(targetProperty, liste.New());
            else
            {
                methode.TrueStatements.Set(targetProperty, new CodeCastExpression(targetProperty.Type, liste.New()));
            }

            if (Model.Instrumentation)
            {
                var If3 = methode.TrueStatements.If(Helper.IsNotNull(PerCounterClass));
                If3.TrueStatements.Add(PerCounterIncrementClass);
            }

            VariableExp current = null;

            TypeExpression myTypeEnum = GetSubType(sourceProperty.Type.TypeExpression, item.SourceItemType);
            TypeExpression myTypeEnum2 = GetSubType(targetProperty.Type.TypeExpression, item.SourceItemType);


            //foreach (T current in source.PropertyName)
            var f2 = methode.TrueStatements.ForEach(myTypeEnum, sourceProperty, out current, "_cur" + myTypeEnum.Name + _cnt.ToString(), "enumerator" + _cnt.ToString());

            CodeStatementCollection code = f2.Statements;
            if (!IsSimpleValue(myTypeEnum2))
            {
                var If11 = f2.Statements.If(Helper.IsNotNull(current));
                code = If11.TrueStatements;
            }

            // copy conditionnelle
            if (!string.IsNullOrEmpty(item.ConditionnalMappingMethod.Trim()))
            {
                CodeConditionStatement f1 = null;

                f1 = code.If(new MethodExp(Helper.This(), item.ConditionnalMappingMethod.Trim()).Call(current));
                code = f1.TrueStatements;
            }


            if (mapper != null)
            {
                code.Add(new CodeCommentStatement(new CodeComment(string.Format("-------------------- External call mapper {0} ({1})--------------------", mapper.Name, sourceProperty.Type.TypeExpression.Name))));
                MethodExp m = GetVariableMapperFor(item.ExternalModelDocument, mapper);
                CodeExpression value = null;
                if (current.Type.TypeExpression.Equals(myTypeEnum))
                    value = m.Call(current);
                else
                    value = m.Call(current).As(targetProperty.Type);

                code.Add(targetProperty.Method("Add").Call(value));
            }
            else
            {
                code.Add(targetProperty.Method("Add").Call(current));
            }



            if (Model.Instrumentation)
            {

                var If5 = methode.TrueStatements.If(Helper.IsNotNull(PerCounterClass));
                If5.TrueStatements.Add(PerCounterDecrementClass);

                var If6 = code.If(Helper.IsNotNull(PerCounterClass));
                If6.TrueStatements.Add(PerCounterDecrementClass);

            }

        }

        private void CopyListeReflexion(CodeStatementCollection method, ItemMap item, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty, Dictionary<string, TypeExp> ListeConverters, Stack<RecurcifBag> queue, VariableExp liste, Type SourceType, Type DestinationType)
        {

            var methode = method.If(Helper.IsNotNull(sourceProperty));

            // target.PropertyName = new List<T>(); 
            if (liste.Type.Equals(targetProperty.Type))
                methode.TrueStatements.Set(targetProperty, liste.New());
            else
                methode.TrueStatements.Set(targetProperty, new CodeCastExpression(targetProperty.Type, liste.New()));

            VariableExp current = null;

            TypeExpression myTypeEnumSource = GetSubType(sourceProperty.Type.TypeExpression, item.SourceItemType);

            //foreach (T current in source.PropertyName)
            var f2 = methode.TrueStatements.ForEach(myTypeEnumSource, sourceProperty, out current, "_cur" + myTypeEnumSource.Name + _cnt.ToString(), "enumerator" + _cnt.ToString());

            CodeStatementCollection code = f2.Statements;

            // si le type est nullable on insere un test de nullité
            if (!IsSimpleValue(SourceType))
            {
                var f11 = f2.Statements.If(Helper.IsNotNull(current));
                code = f11.TrueStatements;
            }

            // copy conditionnelle
            if (!string.IsNullOrEmpty(item.ConditionnalMappingMethod.Trim()))
            {
                CodeConditionStatement f1 = code.If(new MethodExp(Helper.This(), item.ConditionnalMappingMethod.Trim()).Call(current));
                code = f1.TrueStatements;
            }
            

            if (SourceType == DestinationType)
            {
                // test si il y a besoin de caster la liste pour avoir une methode 'Add'
                if (targetProperty.Type.TypeExpression.Equals(liste.Type.TypeExpression))
                    code.Add(targetProperty.Method("Add").Call(current));
                else
                    code.Add(targetProperty.As(liste.Type).Method("Add", liste.Type).Call(current));
            }
            else
            {

                // voir pour le cast
                TypeExpression myTypeEnumTarget = GetSubType(targetProperty.Type.TypeExpression, item.TargetItemType);
                PropertyExp target2 = new PropertyExp
                    (
                        targetProperty,
                        targetProperty.PropertyName + _cnt.ToString(),
                        myTypeEnumTarget
                    );

                code.Declare(target2);

                // test si il y a besoin de caster la liste pour avoir une methode 'Add'
                if (targetProperty.Type.TypeExpression.Equals(liste.Type.TypeExpression))
                    code.Add(targetProperty.Method("Add").Call(target2));
                else
                    code.Add(targetProperty.As(liste.Type).Method("Add", liste.Type).Call(target2));

            }



        }



        private static bool IsSimpleValue(Type type)
        {

            if (type == null)
                return false;

            if (type.IsEnum || MappingHelper.tbTypeValue.Contains(type))
                return true;

            return false;
        }

        private static bool IsSimpleValue(TypeExpression typeExpression)
        {

            string n = typeExpression.Namespace + "." +typeExpression.Name;
            foreach (Type item in MappingHelper.tbTypeValue)
                if (item.FullName == n)
                    return true;
            

            return false;
        }

        private static bool IsSimpleValue(PropertyExp sourceProperty)
        {
            return IsSimpleValue(sourceProperty.Type.TypeExpression);
        }

        private static bool IsSimpleValue(TypeExp typeExp)
        {
            return IsSimpleValue(typeExp.TypeExpression);
        }


        private void CreateEnumerator(CodeStatementCollection method, Mapper mapper, VariableExp source, VariableExp target)
        {

            //Mapper mapper2 = model.FindMapperFor(source.Type.TypeExpression.FullName, target.Type.TypeExpression.FullName);
            Mapper mapper2 = Model.FindMapperFor(mapper.MappingSource.TypeElement.ItemType, mapper.MappingDestination.TypeElement.ItemType);
            if (mapper2 == null)
            {
                method.Add(new CodeCommentStatement(new CodeComment("Cannot find the good mapper")));
                return;
            }

            //VariableExp map2 = new VariableExp(mapper2.Name, TypeExp.Object);

            TypeExpression myTypeEnum = GetSubType(source.Type.TypeExpression, mapper.MappingSource.TypeElement.ItemType);


            VariableExp current = null;
            var f2 = method.ForEach(myTypeEnum, source, out current, "_cur" + myTypeEnum.Name + _cnt.ToString(), "enumerator" + _cnt.ToString());

            myTypeEnum = GetSubType(target.Type.TypeExpression, mapper.MappingDestination.TypeElement.ItemType);

            
            var f1 = f2.Statements.If(Helper.IsNotNull(current));

            VariableExp v1 = f1.TrueStatements.Declare("itemTarget" + _cnt.ToString(), myTypeEnum, MyThis.Method(mapper2.MapMethodName, target.Type).Call(current));

            CodeExpression m = null;
            if (mapper.MappingSource.TypeElement.IsInterface || mapper.MappingSource.TypeElement.IsAbstract)
            {

                //ITemplateModel template
                //    = GetTemplate(TemplateExist(mapper.ModelTemplate)
                //    ? mapper.ModelTemplate
                //    : "TemplateModelDefault");

                ITemplateModel template = GetTemplate("TemplateModelDefault");

                template.Type = target.Type.TypeExpression;
                template.Parameter = "";
                template.MapperName = mapper.Name;
                m = template.InsertItem(v1, target);

            }

            else
            {
                //string methodName = string.IsNullOrEmpty(mapper.ModelTemplateParameter) ? "Add" : mapper.ModelTemplateParameter;
                string methodName = "Add";
                m = target.Method(methodName, target.Type).Call(v1);

            };

            f1.TrueStatements.Add(m);

        }

        private void CreateConverterExternalMapClass(CodeStatementCollection method, ItemMap item, VariableExp source, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty, Dictionary<string, TypeExp> ListeConverters, Stack<RecurcifBag> queue)
        {

            Mapper mapper = Model.FindExternalMapperFor(item.ExternalModelDocument);

            string[] tb = item.ExternalModelDocument.Split('!');
            if (mapper == null)
            {
                method.Add(new CodeCommentStatement(new CodeComment(string.Format("Missing mapper '{1}' in {0}", tb[0], tb[1]))));
                return;
            }

            CodeStatementCollection Cde = method;

            if (!item.SourceIsValueType)
                if (!IsSimpleValue(sourceProperty))
                {
                    var If = method.If(Helper.IsNotNull(sourceProperty));
                    Cde = If.TrueStatements;

                    if (item.NullPolicy == PolicyNullValue.ReturnNewItem)
                    {
                        VariableExp target21 = new VariableExp(target.VariableName + _cnt.ToString(), targetProperty.Type.TypeExpression);
                        CodeExpression expr = CreateTarget(mapper, sourceProperty, target21);
                        If.FalseStatements.Set(targetProperty, expr);
                    }
                }

            MethodExp m = GetVariableMapperFor(item.ExternalModelDocument, mapper);

            Cde.Add(new CodeCommentStatement(new CodeComment(string.Format(" ---- External mapper call '{1}' in {0} ----", tb[0], tb[1]))));
            Cde.Set(targetProperty, m.Call(sourceProperty));
        }

        private MethodExp GetVariableMapperFor(string name, Mapper mapper)
        {
            MethodExp v = null;

            if (_externalMappers.ContainsKey(name))
            {
                v = new MethodExp(_externalMappers[name], mapper.MapMethodName);
            }
            else
            {

                if (mapper.MappingModel.ClassName != Model.ClassName)
                {
                    var v1 = new VariableExp(name.Replace("!", "").Replace(".", ""), mapper.MappingModel.Namespace + "." + mapper.MappingModel.ClassName);
                    _externalMappers.Add(name, v1);
                    v = new MethodExp(v1, mapper.MapMethodName);
                }
                else
                {
                    v = new MethodExp(Helper.This(), mapper.MapMethodName);
                }
            
            }

            return v;
        }

        private void CreateConverterMapClass(CodeStatementCollection method, ItemMap item, VariableExp source, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty, Dictionary<string, TypeExp> ListeConverters, Stack<RecurcifBag> queue)
        {

            Mapper mapper = Model.FindMapperFor(item);

            if (mapper == null)
            {
                method.Add(new CodeCommentStatement(new CodeComment("Missing mapper for convert")));
                method.Add(new CodeCommentStatement(new CodeComment(item.SourceType)));
                method.Add(new CodeCommentStatement(new CodeComment("in")));
                method.Add(new CodeCommentStatement(new CodeComment(item.TargetType)));
                return;
            }

            CodeStatementCollection Cde = method;

            if (!item.SourceIsValueType)
                if (!IsSimpleValue(sourceProperty))
                {
                    var If = method.If(Helper.IsNotNull(sourceProperty));
                    Cde = If.TrueStatements;

                    if (item.NullPolicy == PolicyNullValue.ReturnNewItem)
                    {
                        VariableExp target21 = new VariableExp(target.VariableName + _cnt.ToString(), targetProperty.Type.TypeExpression);
                        CodeExpression expr = CreateTarget(mapper, sourceProperty, target21);
                        If.FalseStatements.Set(targetProperty, expr);
                    }
                }

     

            bool recursif =  QueueContains(queue, mapper);
            if (!recursif)
            {

                Cde.Add(new CodeCommentStatement(new CodeComment(string.Format("-------------------- Start Non recursif {0} ({1})--------------------", mapper.Name, sourceProperty.Type.TypeExpression.Name))));
                
                VariableExp target2 = new VariableExp(target.VariableName + _cnt.ToString(), targetProperty.Type.TypeExpression);
                CodeExpression expr = CreateTarget(mapper, sourceProperty, target2);

                if (item.CreateTarget)
                    Cde.Declare(target2, expr);


                if (Model.Instrumentation)
                {
                    var If3 = Cde.If(Helper.IsNotNull(PerCounterClass));
                    If3.TrueStatements.Add(PerCounterIncrementClass);
                }




                CreateMap(Cde, mapper, ListeConverters, sourceProperty, target2, queue);
                
                if (Model.Instrumentation)
                {
                    var If4 = Cde.If(Helper.IsNotNull(PerCounterClass));
                    If4.TrueStatements.Add(PerCounterDecrementClass);
                }
                Cde.Set(targetProperty, target2);



                Cde.Add(new CodeCommentStatement(new CodeComment(string.Format("-------------------- End {0} ({1})----------------------", mapper.Name, sourceProperty.Type.TypeExpression.Name))));

            }
            else
            {
                Cde.Add(new CodeCommentStatement(new CodeComment(string.Format("-------------------- Start {0} ({1})--------------------", mapper.Name, sourceProperty.Type.TypeExpression.Name))));

                Cde.Add(new CodeCommentStatement(new CodeComment(string.Format("HACK: Recurcif code detected {0}", ShowQueue(queue)))));
                var value = MyThis.Method(mapper.MapMethodName, targetProperty.Type).Call(sourceProperty);
                Cde.Set(targetProperty, value);

                Cde.Add(new CodeCommentStatement(new CodeComment(string.Format("-------------------- End {0} ({1})----------------------", mapper.Name, sourceProperty.Type.TypeExpression.Name))));

            }


            return;
        }

        private bool QueueContains(Stack<RecurcifBag> queue, Mapper mapper)
        {
            foreach (RecurcifBag r in queue)            
                if (r.Mapper == mapper)
                    return true;
            return false;
        }

        private string ShowQueue(Stack<RecurcifBag> queue)
        {

            RecurcifBag last = queue.Last();

            StringBuilder s = new StringBuilder();
            foreach (RecurcifBag item in queue)
            {
                s.AppendFormat("({0})", item.Mapper.Name);

                string t = item.Source.VariableName;
                s.Append(t);

                if (item != last)
                    s.Append(" > ");
            }

            return s.ToString();
        }

        #region Converters
        
        private TypeExp CreateTypeConverterMapProperty(CodeStatementCollection methode, ItemMap item, VariableExp source, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty)
        {

            CodeStatementCollection method = methode;

            if (item.SourceIsClass && !(sourceProperty.Type.TypeExpression.Equals(typeof(string))))
            {
                var if1 = method.If(Helper.IsNotNull(sourceProperty));
                method = if1.TrueStatements;
            }
            else if (sourceProperty.PropertyName.Contains("."))
            {

                CodeConditionStatement if2 = null;
                string[] tb = sourceProperty.PropertyName.Split('.');
                CodeExpression source1 = sourceProperty.TargetObject;

                for (int i1 = 0; i1 < tb.Length - 1; i1++)
                {

                    source = new PropertyExp(source1, tb[i1], typeof(object));
                    if2 = method.If(Helper.IsNotNull(source1));
                    method = if2.TrueStatements;

                }

            }

            TypeExp i = new TypeExp(item.Cast);
            string n = ConvertInCSharpName(i.TypeExpression.Name);
            var castObject = MyThis.Field("_C" + n, typeof(TypeConverter));

            string methodName = "ConvertFrom";


            method.SetWithCast(
                targetProperty,
                castObject.Method(methodName, targetProperty.Type).Call(sourceProperty)
            , targetProperty.Type
            );

            return i;
        }

        private TypeExp CreateIValueConverterMapProperty(CodeStatementCollection methode, ItemMap item, VariableExp source, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty)
        {


            CodeStatementCollection method = methode;

            if (item.SourceIsClass && !(sourceProperty.Type.TypeExpression.Equals(typeof(string))))
            {
                var if1 = method.If(Helper.IsNotNull(sourceProperty));
                method = if1.TrueStatements;
            }
            else if (sourceProperty.PropertyName.Contains("."))
            {

                CodeExpression source1 = sourceProperty.TargetObject;
                CodeConditionStatement if2 = null;
                string[] tb = sourceProperty.PropertyName.Split('.');
                for (int i1 = 0; i1 < tb.Length - 1; i1++)
                {

                    source = new PropertyExp(source1, tb[i1], typeof(object));
                    if2 = method.If(Helper.IsNotNull(source1));
                    method = if2.TrueStatements;

                }

            }

            TypeExp i = new TypeExp(item.Cast);
            string n = ConvertInCSharpName(i.TypeExpression.Name);
            var castObject = MyThis.Field("_C" + n, "IValueConverter");

            string methodName = String.IsNullOrEmpty(item.CastWay) ? "Convert" : item.CastWay;
            var currentCulture = new CodeSnippetExpression("System.Globalization.CultureInfo.CurrentCulture");

            method.SetWithCast(
                targetProperty,
                castObject.Method(methodName, targetProperty.Type).Call
                (sourceProperty, 
                 i.TypeOf, 
                 currentCulture)
            , targetProperty.Type
            );

            return i;
        }

        private TypeExp CreateIConverterMapProperty(CodeStatementCollection methode, ItemMap item, VariableExp source, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty)
        {


            CodeStatementCollection method = methode;

            if (item.SourceIsClass && !(sourceProperty.Type.TypeExpression.Equals(typeof(string))))
            {
                var if1 = method.If(Helper.IsNotNull(sourceProperty));
                method = if1.TrueStatements;
            }
            else if (sourceProperty.PropertyName.Contains("."))
            {

                CodeExpression source1 = sourceProperty.TargetObject;
                CodeConditionStatement if2 = null;
                string[] tb = sourceProperty.PropertyName.Split('.');
                for (int i1 = 0; i1 < tb.Length - 1; i1++)
                {

                    source = new PropertyExp(source1, tb[i1], typeof(object));
                    if2 = method.If(Helper.IsNotNull(source1));
                    method = if2.TrueStatements;

                }

            }


            TypeExp i = new TypeExp(item.Cast);

            string n = ConvertInCSharpName(i.TypeExpression.Name);
            var castObject = MyThis.Field("_C" + n, "IValueConverter");

            string methodName = String.IsNullOrEmpty(item.CastWay) ? "Convert" : item.CastWay;

            var currentCulture = new CodeSnippetExpression("System.Globalization.CultureInfo.CurrentCulture");

            method.SetWithCast
                (
                  targetProperty,
                  castObject.Method(methodName).Call(sourceProperty,
                  i.TypeOf,
                  currentCulture
                )
                , targetProperty.Type);

            return i;
        }

        private void CreateExplicitProperty(CodeStatementCollection methode, ItemMap item, VariableExp source, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty)
        {

            CodeStatementCollection method = methode;

            if (item.SourceIsClass && !(sourceProperty.Type.TypeExpression.Equals(typeof(string))))
            {
                if (!IsSimpleValue(sourceProperty) && !item.SourceIsValueType && !item.SourceIsEnum)
                {
                    var if1 = method.If(Helper.IsNotNull(sourceProperty));
                    method = if1.TrueStatements;
                }
            }
            else if (sourceProperty.PropertyName.Contains("."))
            {

                CodeExpression source1 = sourceProperty.TargetObject;
                CodeConditionStatement if2 = null;
                string[] tb = sourceProperty.PropertyName.Split('.');
                for (int i = 0; i < tb.Length - 1; i++)
                {

                    source = new PropertyExp(source1, tb[i], typeof(object));
                    if2 = method.If(Helper.IsNotNull(source1));
                    method = if2.TrueStatements;

                }


            }

            System.CodeDom.CodeExpression s = null;       

            if (TryGetSpecificCast(sourceProperty, targetProperty, item, out s))
                method.Set(targetProperty, s);

            else
                method.SetWithCastIfNeed(targetProperty, sourceProperty);

        }

        private void CreateCustomMapProperty(CodeStatementCollection method, ItemMap item, VariableExp source, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty)
        {

            string name = string.IsNullOrEmpty(item.CustomMethodName)
            ? ConvertInCSharpName("C" + (sourceProperty.Type.TypeExpression.Name + "To" + targetProperty.Type.TypeExpression.Name))
            : item.CustomMethodName;

            CodeExpression retour = null;

            method.Add(new CodeCommentStatement(new CodeComment("attention, the signature of call to customs method has changed and includes only one parameter.")));
            if (sourceProperty.PropertyName == "this")
                retour = MyThis.Method(name, targetProperty.Type).Call(sourceProperty.root);            
            else            
                retour = MyThis.Method(name, targetProperty.Type).Call(sourceProperty);


            // cast le retour dans le type attendu.
            if (item.CastReturnCustomMethod)
                method.Set(targetProperty, Helper.Cast(retour, targetProperty.Type));
            else
                method.Set(targetProperty, retour);

        }

        #endregion

        private CodeExpression CreateTarget(Mapper mapper, CodeExpression source, VariableExp target)
        {

            CodeExpression m;
            var e = mapper.MappingDestination.TypeElement;

            string nname = "Create";
            if (!string.IsNullOrEmpty(mapper.CustomConstructorMethodName))
                nname = mapper.CustomConstructorMethodName;

            if (mapper.CustomConstructor)
            {
                m = MyThis.Method(nname, target.Type).Call(source);
            }
            else
            {
                if (e.IsInterface || e.IsAbstract)
                {
                    //if (!TemplateExist(mapper.ModelTemplate))
                    //{
                        m = MyThis.Method(nname, target.Type).Call(source);
                    //}
                    //else
                    //{
                    //    ITemplateModel template = GetTemplate(mapper.ModelTemplate);
                    //    template.Type = target.Type.TypeExpression;
                    //    template.MapperName = mapper.Name;
                    //    template.Parameter = mapper.ModelTemplateParameter;
                    //    m = template.CreateObject();
                    //}
                }
                else
                    m = new CodeObjectCreateExpression(target.Type.TypeExpression.ToString());

            }
            return m;
            
        }

        private CodeExpression CreateEnumerable(Mapper mapper, VariableExp target)
        {

            CodeExpression m;
            var e = mapper.MappingDestination.TypeElement;

            ITemplateModel template = GetTemplate("TemplateModelList");
                template.Type = target.Type.TypeExpression;
                template.MapperName = mapper.Name;
                template.Parameter = ""; // mapper.ModelTemplateParameter;
                m = template.CreateObject();

                return m;
        }

        public bool TemplateExist(string modelTemplate)
        {
            if (string.IsNullOrEmpty(modelTemplate))
                return false;
            List<string> lst = typeof(ITemplateModel).Assembly.GetTypes()
                    .Where(c =>
                        c.IsClass &&
                        !c.IsAbstract &&
                        c.IsOfType(false, typeof(ITemplateModel)
                        )
                ).Select(d => d.Name).ToList();
            return (lst.Contains(modelTemplate));
        }

        private ITemplateModel GetTemplate(string modelTemplate)
        {
            Type t = typeof(ITemplateModel).Assembly.GetTypes()
                    .Where(c =>
                        c.IsClass &&
                        !c.IsAbstract &&
                        c.IsOfType(false, typeof(ITemplateModel)) &&
                        c.Name == modelTemplate                        
                ).FirstOrDefault();

            return (ITemplateModel)Activator.CreateInstance(t);
        }

        private TypeMember CreateClass(Mapper mapper)
        {

            TypeMember Class = Helper.CreateClass(AutoMapperName, true, false);

            Class.IsPartial = true;

            return Class;
        }

        private Type FindType(string typeName)
        {

            if (_dicTypes.ContainsKey(typeName))
                return _dicTypes[typeName];

            Type type = null;

            if (!typeName.Contains("["))
            {

                foreach (Type item in ContainerTypes.Types)
                    if (item.FullName == typeName)
                    {
                        type = item;
                        break;
                    }

                if (type == null)
                    type = Type.GetType(typeName);

            }
            else
            {
                var e = (TypeExpression)typeName;
                type = e.Evaluate(c => FindType(c));
            }

            _dicTypes.Add(typeName, type);

            return type;
        }


        private Dictionary<string, Type> _dicTypes = new Dictionary<string, Type>();


        private const string Pattern1 = @"[^\w\d]*";
        private static Regex regEx = new Regex(Pattern1, RegexOptions.Compiled);
        private List<string> integratedMapper;
        public static string ConvertInCSharpName(string i) { return regEx.Replace(i, string.Empty); }

        public MappingModel Model { get; set; }

    }

    [System.Diagnostics.DebuggerDisplay("{Mapper.Name}")]
    [CLSCompliant(false)]
    public class RecurcifBag
    {
        public Mapper Mapper { get; set; }

        public VariableExp Source { get; set; }

        public int Count { get; set; }
    }

    [CLSCompliant(false)]
    public class IndexBag
    {

        public List<ItemMap> Properties { get; set; }

        public VariableExp Variable { get; set; }
    }


}
